package com.bicon.botu.finance.payment.service;

import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.internal.util.StringUtils;

import com.bicon.botu.finance.payment.entity.*;
import com.bicon.botu.finance.payment.common.PaymentConstants;
import com.bicon.botu.finance.payment.repository.AppPaymentInfoRepository;
import com.bicon.botu.finance.payment.repository.NotifyAppResultRepository;
import com.bicon.botu.finance.payment.repository.PayExcepetionInfoRepository;
import com.bicon.botu.finance.payment.repository.PayInfoRepository;
import com.bicon.botu.finance.payment.unionpay.UnionPayConfig;
import com.bicon.botu.finance.payment.unionpay.sdk.AcpService;
import com.bicon.botu.finance.payment.unionpay.sdk.LogUtil;
import com.bicon.botu.finance.payment.unionpay.sdk.SDKConstants;
import com.bicon.botu.finance.payment.weixin.utils.HttpUtil;
import lombok.extern.log4j.Log4j;
import org.apache.commons.collections.map.HashedMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;


/**
 * Created by L on 2017/6/22.
 */
@Log4j
@Service
public class PayNotifyService {

    @Autowired
    private AppPaymentInfoRepository appPaymentInfoRepository;

    @Autowired
    private PayInfoRepository payInfoRepository;

    @Autowired
    private PayExcepetionInfoRepository payExcepetionInfoRepository;

    @Autowired
    private NotifyAppResultRepository notifyAppResultRepository;

    @Autowired
    private KafkaTemplate kafkaTemplate;

    /**
     * 处理支付结果通知
     *
     * @param map 支付宝回传参数
     */
    public String handlerAliPayResult(Map<String, String> map) {
        try {
            String code = map.get("passback_params");
            String orderNo = map.get("out_trade_no");
            String payMoney = map.get("buyer_pay_amount");
            String payNo = map.get("trade_no");
            /*JSONObject jasonObject = JSONObject.parseObject(params);
            Map<String, String> map1 = (Map) jasonObject;
            String code = map1.get("code");*/
            if (code == null || orderNo == null) {
                //walletRpc.recahrgeResult(orderNo, PaymentConstants.PAY_STATUS_FAIL,payMoney);
                return "fail";
            }
            AppPaymentInfoEntity infoEntity = appPaymentInfoRepository.findOneByCode(code);
            if (infoEntity == null) {
                //返回结果的code不匹配
                //walletRpc.recahrgeResult(orderNo, PaymentConstants.PAY_STATUS_FAIL,payMoney);
                return "fail";
            }
            boolean flag = AlipaySignature.rsaCheckV1(map, infoEntity.getAliPayDto().getAlipayPublicKey(), "utf-8", "RSA2");
            if (flag) {
                //支付成功
                PayInfoEntity payInfoEntity = payInfoRepository.findOneByOrderNoAndCode(orderNo, code);
                if (payInfoEntity == null) {
                    payExcepetion(orderNo, code, payMoney, payNo, AliPayDto.TYPE_ALIPAY, "没有根据订单编号和APP CODE查询到支付信息");
                    //walletRpc.recahrgeResult(orderNo, PaymentConstants.PAY_STATUS_FAIL,payMoney);
                    return "fail";
                }
                if (payInfoEntity.getPayResult() == PayInfoEntity.PAY_RESULT_SUCCESS) {
                    //已经支付成功了
                    //walletRpc.recahrgeResult(orderNo, PaymentConstants.PAY_STATUS_SUCCESS,payMoney);
                    return "success";
                }
                float orderMoney = Float.parseFloat(payInfoEntity.getOrderMoney());
                float payMoneyFloat=0.0f;
                if(null!=payMoney){
                    payMoneyFloat = Float.parseFloat(payMoney);
                }
                log.info("【支付宝充值金额】"+payMoneyFloat);
                if (payMoneyFloat == orderMoney) {
                    paySuccess(code, orderNo, payMoney, payNo);
                } else {
                    //支付金额不匹配
                    payInfoExcepetion(code, orderNo, payMoney, payNo);
                }
                String attr = payInfoEntity.getAttr();
                String url = infoEntity.getNotifyUrl();
                Map<String, String> notifyMap = new HashedMap();
                notifyMap.put("attr", attr);
                notifyMap.put("orderNo", orderNo);
                notifyMap.put("code", code);
                notifyMap.put("money", payMoney);
                notifyAppService(url, notifyMap);
                return "success";
            } else {
                //支付结果校验失败
                payInfoExcepetion(code, orderNo, payMoney, payNo);
                payExcepetion(orderNo, code, payMoney, payNo, AliPayDto.TYPE_ALIPAY, "支付宝校验失败");
                //walletRpc.recahrgeResult(orderNo, PaymentConstants.PAY_STATUS_FAIL,payMoney);
                return "fail";
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            //walletRpc.recahrgeResult(orderNo, PaymentConstants.PAY_STATUS_FAIL,payMoney);
            return "fail";
        }
    }


    /**
     * 处理微信支付结果通知
     *
     * @param resMp
     * @param result
     * @return
     */
    public String wxpay(Map<String, String> resMp, String result) {
        //微信支付成功确认返回内容
        String ret = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";


        if (com.bicon.botu.core.base.common.utils.StringUtils.isBlank(result)) {
            log.info("异常请求-支付参数丢失");
            return "fail";
        }
        String return_code = resMp.get("return_code");
        String result_code = resMp.get("result_code");
        if (return_code.equals("SUCCESS") && result_code.equals("SUCCESS")) {
            String code = resMp.get("attach");
            String orderNo = resMp.get("out_trade_no");
            String cashFee = resMp.get("cash_fee");
            String payId = resMp.get("transaction_id");
            if (orderNo == null || cashFee == null) {
                return "fail";
            }
            if (StringUtils.isEmpty(code)) {
                //app code丢失
                log.info("异常请求-app code丢失");
                return "fail";
            }
            //对订单及金额进行检查，并作对应处理
            boolean check = checkPayNotify(code, orderNo, cashFee, payId, WechatPayDto.TYPE_WECHAT);
            if (check) {
                return ret;
            } else {
                payExcepetion(orderNo, code, cashFee, payId, WechatPayDto.TYPE_WECHAT, "微信校验失败");
                log.info("异常请求-微信校验失败-订单及金额检查失败");
                return "fail";
            }

        }
        return "fail";
    }

    /**
     * 银联提现异步通知
     *
     * @param req
     */
    public boolean handleUnionPaymentCallBack(HttpServletRequest req) {
        LogUtil.writeLog("BackRcvResponse接收后台通知开始");

        String encoding = req.getParameter(SDKConstants.param_encoding);
        // 获取银联通知服务器发送的后台通知参数
        Map<String, String> reqParam = UnionPayConfig.getAllRequestParam(req);

        LogUtil.printRequestLog(reqParam);
        //对回传参数进行整理
        Map<String, String> valideData = UnionPayConfig.getDateOfMap(reqParam);

        //重要！验证签名前不要修改reqParam中的键值对的内容，否则会验签不过
        if (!AcpService.validate(valideData, encoding)) {
            LogUtil.writeLog("验证签名结果[失败].");
            //验签失败，需解决验签问题
            String orderId = valideData.get("orderId"); //订单号
            String code = valideData.get("reqReserved"); //透传字段
            String queryId = valideData.get("queryId"); //交易查询流水号
            String txnAmt = valideData.get("txnAmt"); //交易金额
            payExcepetion(orderId, code, txnAmt, queryId, UnionpayDto.TYPE_UNION, "银联校验失败");
            //walletRpc.recahrgeResult(orderId, PaymentConstants.PAY_STATUS_FAIL, txnAmt);
            return false;
        } else {
            LogUtil.writeLog("验证签名结果[成功].");
            //【注：为了安全验签成功才应该写商户的成功处理逻辑】交易成功，更新商户订单状态
            String respCode = valideData.get("respCode");
            //判断respCode=00、A6后，对涉及资金类的交易，请再发起查询接口查询，确定交易成功后更新数据库。
            if (("00").equals(respCode)) {//如果查询交易成功
                String origRespCode = valideData.get("origRespCode");
                String orderId = valideData.get("orderId"); //订单号
                String code = valideData.get("reqReserved"); //透传字段
                String queryId = valideData.get("queryId"); //交易查询流水号
                String txnAmt = valideData.get("txnAmt"); //交易金额
                if (("00").equals(origRespCode)) {
                    //交易成功，更新商户订单状态
                    //对订单及金额进行检查，并作对应处理
                    boolean check = checkPayNotify(code, orderId, txnAmt, queryId, WechatPayDto.TYPE_WECHAT);
                    //walletRpc.recahrgeResult(orderId, PaymentConstants.PAY_STATUS_FAIL, txnAmt);
                    return check;
                } else {
                    //其他应答码为交易失败
                    //walletRpc.recahrgeResult(orderId, PaymentConstants.PAY_STATUS_FAIL, txnAmt);
                    return false;
                }
            } else {//查询交易本身失败，如应答码10/11检查查询报文是否正确
                //walletRpc.recahrgeResult(orderId, PaymentConstants.PAY_STATUS_FAIL,txnAmt);
                return false;
            }
        }
    }

    /**
     * 银联支付异步通知
     *
     * @param req
     */
    public boolean handleUnionTransferCallBack(HttpServletRequest req) {
        LogUtil.writeLog("BackRcvResponse接收后台通知开始");

        String encoding = req.getParameter(SDKConstants.param_encoding);
        // 获取银联通知服务器发送的后台通知参数
        Map<String, String> reqParam = UnionPayConfig.getAllRequestParam(req);

        LogUtil.printRequestLog(reqParam);
        //对回传参数进行整理
        Map<String, String> valideData = UnionPayConfig.getDateOfMap(reqParam);

        //重要！验证签名前不要修改reqParam中的键值对的内容，否则会验签不过
        if (!AcpService.validate(valideData, encoding)) {
            LogUtil.writeLog("验证签名结果[失败].");
            //验签失败，需解决验签问题
            String orderId = valideData.get("orderId"); //订单号
            String code = valideData.get("reqReserved"); //透传字段
            String queryId = valideData.get("queryId"); //交易查询流水号
            String txnAmt = valideData.get("txnAmt"); //交易金额
            payExcepetion(orderId, code, txnAmt, queryId, UnionpayDto.TYPE_UNION, "银联校验失败");
            return false;
        } else {
            LogUtil.writeLog("验证签名结果[成功].");
            //【注：为了安全验签成功才应该写商户的成功处理逻辑】交易成功，更新商户订单状态
            String respCode = valideData.get("respCode");
            //判断respCode=00、A6后，对涉及资金类的交易，请再发起查询接口查询，确定交易成功后更新数据库。
            if (("00").equals(respCode)) {//如果查询交易成功
                String origRespCode = valideData.get("origRespCode");
                String orderId = valideData.get("orderId"); //订单号
                String code = valideData.get("reqReserved"); //透传字段
                String queryId = valideData.get("queryId"); //交易查询流水号
                String txnAmt = valideData.get("txnAmt"); //交易金额
                if (("00").equals(origRespCode)) {
                    //交易成功，更新商户订单状态
                    //对订单及金额进行检查，并作对应处理
                    boolean check = checkPayNotify(code, orderId, txnAmt, queryId, WechatPayDto.TYPE_WECHAT);
                    return check;
                } else {
                    //其他应答码为交易失败
                    return false;
                }
            } else {//查询交易本身失败，如应答码10/11检查查询报文是否正确
                return false;
            }
        }
    }

    private void notifyAppService(String url, Map<String, String> map) {
        //通知失败！
        NotifyAppResultEntity resultEntity;
        String code = map.get("code");
        String orderNo = map.get("orderNo");
        String result = HttpUtil.sendPost(url, map, "utf-8");
        if (result == null || !result.equals("success")) {
            resultEntity = new NotifyAppResultEntity(url, code, orderNo, NotifyAppResultEntity.FAIL);
        } else {
            resultEntity = new NotifyAppResultEntity(url, code, orderNo, NotifyAppResultEntity.SUCCESS);
        }
        resultEntity.preInsert();
        notifyAppResultRepository.save(resultEntity);
    }


    /**
     * 检查异步通知的支付信息
     *
     * @param code
     * @param orderId
     * @param paymoney
     * @param queryId
     * @param type
     * @return
     */
    private boolean checkPayNotify(String code, String orderId, String paymoney, String queryId, String type) {
        float money = Float.parseFloat(paymoney);
        PayInfoEntity payInfoEntity = payInfoRepository.findOneByOrderNoAndCode(orderId, code);
        if (payInfoEntity == null) {
            //返回参数有误
            log.info("返回参数有误");
            payExcepetion(orderId, code, paymoney, queryId, type, "返回的APP CODE和订单号没有查到支付信息");
            return false;
        }
        if (payInfoEntity.getPayResult() == PayInfoEntity.PAY_RESULT_SUCCESS) {
            return true;
        }
        float orderMoney = Float.parseFloat(payInfoEntity.getOrderMoney());
        //判断金额是否一致
        if (type != AliPayDto.TYPE_ALIPAY) {
            money = money / 100;
        }
        if (orderMoney == money) {
            paySuccess(code, orderId, String.valueOf(money), queryId);
            return true;
        } else {
            payInfoExcepetion(code, orderId, String.valueOf(money), queryId);
            return false;
        }
    }

    /**
     * 支付成功，更新支付信息
     *
     * @param code     App CODE
     * @param orderNum 订单编号
     * @param money    订单金额
     * @param payNo    支付交易流水号
     */
    private boolean paySuccess(String code, String orderNum, String money, String payNo) {
        try {
            PayInfoEntity payInfoEntity = payInfoRepository.findOneByOrderNoAndCode(orderNum, code);
            if (payInfoEntity == null) {
                return false;
            }
            payInfoEntity.setPayResult(PayInfoEntity.PAY_RESULT_SUCCESS);
            payInfoEntity.setPayMoney(money);
            payInfoEntity.setPayNo(payNo);
            payInfoRepository.save(payInfoEntity);
            return true;
        } catch (Exception e) {
            log.info("error", e);
            return false;
        }

    }

    /**
     * 支付失败，更新支付信息
     *
     * @param code     App CODE
     * @param orderNum 订单编号
     */
    private boolean payFail(String code, String orderNum) {
        return payFail(code, orderNum, null, null);
    }

    /**
     * 支付信息校验异常
     *
     * @param orderNo
     * @param code
     * @param payMoney
     * @param payNo
     * @return
     */
    private boolean payExcepetion(String orderNo, String code, String payMoney, String payNo, String type, String remark) {
        try {
            PayExcepetionInfoEntity excepetionInfoEntity = new PayExcepetionInfoEntity(orderNo, payMoney, type, code, payNo, remark);
            excepetionInfoEntity.preInsert();
            payExcepetionInfoRepository.save(excepetionInfoEntity);
            return true;
        } catch (Exception e) {
            log.info("error", e);
            return false;
        }

    }

    /**
     * 支付异常，更新支付信息
     *
     * @param code     App CODE
     * @param orderNum 订单编号
     * @param paymoney 支付金额
     * @param payNo    支付交易号
     */
    private boolean payInfoExcepetion(String code, String orderNum, String paymoney, String payNo) {
        try {
            PayInfoEntity payInfoEntity = payInfoRepository.findOneByOrderNoAndCode(orderNum, code);
            if (payInfoEntity == null) {
                return false;
            }
            payInfoEntity.setPayResult(PayInfoEntity.PAY_RESULT_EXCEPTION);
            payInfoEntity.setPayMoney(paymoney);
            payInfoEntity.setPayNo(payNo);
            payInfoRepository.save(payInfoEntity);
            return true;
        } catch (Exception e) {
            log.info("error", e);
            return false;
        }
    }

    /**
     * 支付失败，更新支付信息
     *
     * @param code     App CODE
     * @param orderNum 订单编号
     * @param paymoney 支付金额
     * @param payNo    支付交易号
     */
    private boolean payFail(String code, String orderNum, String paymoney, String payNo) {
        try {
            PayInfoEntity payInfoEntity = payInfoRepository.findOneByOrderNoAndCode(orderNum, code);
            if (payInfoEntity == null) {
                return false;
            }
            payInfoEntity.setPayResult(PayInfoEntity.PAY_RESULT_FAIL);
            payInfoEntity.setPayMoney(paymoney);
            payInfoEntity.setPayNo(payNo);
            payInfoRepository.save(payInfoEntity);
            return true;
        } catch (Exception e) {
            log.info("error", e);
            return false;
        }
    }
}
